package edu.hawaii.myisern.action;

import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBException;

import edu.hawaii.myisern.collaborations.jaxb.CollaboratingOrganizations;
import edu.hawaii.myisern.collaborations.jaxb.Collaboration;
import edu.hawaii.myisern.collaborations.jaxb.CollaborationTypes;
import edu.hawaii.myisern.collaborations.jaxb.OutcomeTypes;
import edu.hawaii.myisern.collaborations.jaxb.Years;
import edu.hawaii.myisern.model.Id;
import edu.hawaii.myisern.model.IsernManager;
import edu.hawaii.myisern.organizations.jaxb.Organization;

import net.sourceforge.stripes.action.DontValidate;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.RedirectResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.util.Log;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidateNestedProperties;

/**
 * Handles request from to create or update a collaboration object.
 * 
 * @author Lisa Chen
 * @author Kevin English
 * @author Shaoxuan Zhang
 */
public class CollaborationFormActionBean extends IsernActionBean {
  private static final String COLLABORATION_FORM = "/collaboration_form.jsp";
  /** A private copy of the log **/
  private static Log log = Log.getInstance(CollaborationFormActionBean.class);
  /** An error string, always displayed, but invisible if empty. */
  private String errorMessage = "";
  /** Singleton for use by action bean only. */
  // private static IsernManager isern = new IsernManager();
  @ValidateNestedProperties( { @Validate(field = "name", required = true) })
  private Collaboration collaboration;
  private String id;
  /** Lists of strings since JAXB detail records do no map nicely to stripes tags. **/
  private List<String> collaboratingOrganizations;
  private List<String> collaborationYears;  
  private String collaborationTypes;    
  private String outcomeTypes;  
  private String currentOrganization = null;
  private String existedOrganization = null;

  /**
   * Extract CurrentOrganization. 
   * @return a String of CurrentOrganization.
   */
  public String getCurrentOrganization() {
    return this.currentOrganization;
  }
  /**
   * Set the CurrentOrganization.
   * 
   * @param organization a String of an Organization name.
   */
  public void setCurrentOrganization(String organization) {
    this.currentOrganization = organization;
  }
  /**
   * Extract CurrentOrganization. 
   * @return a String of CurrentOrganization.
   */
  public String getExistedOrganization() {
    return this.existedOrganization;
  }
  /**
   * Set the CurrentOrganization.
   * 
   * @param organization a String of an Organization name.
   */
  public void setExistedOrganization(String organization) {
    this.existedOrganization = organization;
  }
  /**
   * Extract collaboration years. 
   * @return a list of collabration years.
   */
  public List<String> getCollaborationYears() {
    List<String> years = new ArrayList<String>();
    for (BigInteger y : this.collaboration.getYears().getYear() ) {
      years.add(y.toString()) ; 
    }
    return years;
  }
  /**
   * Set the collaboration types.
   * 
   * @param collaborationTypes A String represent a list of collaboration types.
   */
  public void setCollaborationTypes(String collaborationTypes) {
    this.collaborationTypes = collaborationTypes;
  }
  
  /**
   * Get the collaboration types.
   * @return a list of collabration types.
   */
  /*
  public List<String> getCollaborationTypes() {
    if (this.collaboration != null && this.collaboration.getCollaborationTypes() != null) {
      return this.collaboration.getCollaborationTypes().getCollaborationType();
    }
    return new ArrayList<String>();
  }
   */
  
  /**
   * Get the collaboration types.
   * @return a list of collabration types.
   */
  public String getCollaborationTypes() {
    if (this.collaboration != null && this.collaboration.getCollaborationTypes() != null) {
      return IsernManager.
        joinListToString(this.collaboration.getCollaborationTypes().getCollaborationType(), "\n");
    }
    return "";
  }
  
  /**
   * Set the Outcome types.
   * 
   * @param newTypes A list of strings.
   */
  public void setOutcomeTypes(String newTypes) {
    log.debug("setting outcome types !!! " + newTypes);      
    this.outcomeTypes = newTypes;
  }
  
  /**
   * Get the Outcome types.
   * @return a list of Outcome types.
   */
  /*
  public List<String> getOutcomeTypes() {
    log.debug("getting outcome types !!! ");  
    if (this.collaboration != null && this.collaboration.getOutcomeTypes() != null) {
      return this.collaboration.getOutcomeTypes().getOutcomeType();
    }
    return new ArrayList<String>();
  }  
  */
  
  /**
   * Get the Outcome types String.
   * @return a string of Outcome types.
   */
  public String getOutcomeTypes() {
    log.debug("getting outcome types !!! ");  
    if (this.collaboration != null && this.collaboration.getOutcomeTypes() != null) {
      return IsernManager.joinListToString(collaboration.getOutcomeTypes().getOutcomeType(),"\n");
    }
    return "";
  }  
  
  /**
   * Set the collaboration years.
   * 
   * @param newYears A valid collaboration object.
   */
  public void setCollaborationYears(List<String> newYears) {
    this.collaborationYears = newYears;
  }  
  
  /**
   * Get the Collaborating Organizations.
   * @return a list of Collaborating Organizations.
   */
  public List<String> getCollaboratingOrganizations() {
    if (this.collaboratingOrganizations == null) {
      this.collaboratingOrganizations = new ArrayList<String>();
      if (collaboration != null && collaboration.getCollaboratingOrganizations() != null) {
        for (String org : 
            this.collaboration.getCollaboratingOrganizations().getCollaboratingOrganization() ) {
          this.collaboratingOrganizations.add(org) ; 
        }
      }
    }
    return this.collaboratingOrganizations;
  }
  
  /**
   * Set the Collaborating Organizations with a given list of organization names.
   * @param organizations a list of organization names.
   */
  public void setCollaboratingOrganizations(List<String> organizations) {
    this.collaboratingOrganizations = new ArrayList<String>();
    for (String org : organizations) {
      if (!this.collaboratingOrganizations.contains(org)) {
        this.collaboratingOrganizations.add(org) ; 
      }
    }
  }
  
  /**
   * Gets the Collaborating Organization objects.
   * 
   * @return  A list of the collaborating organization objects.
   */
  public List<Organization> getCollaboratingOrganizationObjects() {
    List<Organization> orgList = new ArrayList<Organization>();
    if (this.collaboration != null && this.collaboration.getCollaboratingOrganizations() != null) {
      IsernManager im = new IsernManager();
      for (String orgName : this.collaboration.getCollaboratingOrganizations()
                                  .getCollaboratingOrganization()) {
        orgList.add(im.getOrganization(new Id(orgName)));
      }
    }
    return orgList;
  }
  
  /**
   * Set the Collaborating Organizations.
   * 
   * @param orgs A  valdid list of strings.
   */
  /*
  public void setCollaboratingOrganizations(List<String> orgs) {
    this.collaboratingOrganizations = orgs;
  }  
  */
  
  /**
   * Get the collaboration.
   * @return A collaboration
   */
  public Collaboration getCollaboration() {
    return this.collaboration;
  }

  /**
   * Set the collaboration object.
   * @param collaboration A valid collaboration object.
   */
  public void setCollaboration(Collaboration collaboration) {
    this.collaboration = collaboration;
  }

  /**
   * Set the Id string used to identify a record.
   * @param id The id, usually the name.
   */
  public void setId(String id) {
    this.id = id;
  }

  /**
   * Get the Id string used to identify a record.
   * 
   * @return The Id string.
   */
  public String getId() {
    return this.id;
  }

  /**
   * Returns the error message.
   * 
   * @return The error message.
   */
  public String getErrorMessage() {
    return this.errorMessage;
  }
  
  /**
   * For new collaboration, forward the user onto the jsp form.
   * 
   * @return The next jsp page.
   */
  /*
  @DefaultHandler
  public Resolution newCollaboration() {
    return new ForwardResolution(COLLABORATION_FORM);
  }
  */

  /**
   * Loads a collaboration on to the form ready for editing.
   * 
   * @return The next jsp page.
   */
  @DontValidate
  public Resolution preEdit() {
    IsernManager im = new IsernManager();
    this.collaboration = im.getCollaboration(new Id(this.id));
    return new ForwardResolution(COLLABORATION_FORM);
  }
  
  /**
   * Add the organization to Collaborating Organizations.
   * 
   * @return The next jsp page.
   */
  @DontValidate
  public Resolution addOrganization() {
    if (this.collaboratingOrganizations == null) {
      this.collaboratingOrganizations = new ArrayList<String>();
    }
    if (this.currentOrganization != null && this.currentOrganization.length() > 0 
        && !this.collaboratingOrganizations.contains(this.currentOrganization)) {
      this.collaboratingOrganizations.add(this.currentOrganization);
    }
    return new ForwardResolution(COLLABORATION_FORM);
    //return new RedirectResolution(COLLABORATION_FORM).flash(this);
  }

  /**
   * Remove the organization from Collaborating Organizations.
   * 
   * @return The next jsp page.
   */
  @DontValidate
  public Resolution removeOrganization() {
    if (this.collaboratingOrganizations != null) {
      this.collaboratingOrganizations.remove(this.existedOrganization);   
    }
    return new ForwardResolution(COLLABORATION_FORM);
  }
  
  /**
   * Loads a collaboration on to the form ready for view.
   * 
   * @return The next jsp page.
   */
  @DontValidate
  public Resolution view() {
    IsernManager im = new IsernManager();
    this.collaboration = im.getCollaboration(new Id(this.id));
    if (this.collaboration == null) {
      this.errorMessage = "Collaboration [" + this.id + "] not found.\n" +
          "Add it here or go back to check the source you click from.";
      this.id = null;
      return new ForwardResolution(COLLABORATION_FORM);
    }
    return new ForwardResolution("/collaboration_view.jsp");
  }

  /**
   * Remove the collaboration.
   * @return The next jsp page.
   */
  @DontValidate
  public Resolution remove() {
    IsernManager im = new IsernManager();
    try {
      im.remove(new Id(this.id));
      try {
        im.save();
      }
      catch (IOException e) {
        this.errorMessage = "Error: cannot save(),IO Exception";
        e.printStackTrace();
      }
      catch (JAXBException e) {
        this.errorMessage = "Error: cannot save(),JAXB Exception";
        e.printStackTrace();
      }
    }
    catch (IOException e) {
      this.errorMessage = "Error: cannot remove(),IO Exception";
      e.printStackTrace();
    }
    return new ForwardResolution("/collaborations.jsp");
  }
  
  /**
   * Saves (or updates) the collaboration.
   * 
   * @return The next jsp page.
   */
  public Resolution save() {
    IsernManager im = new IsernManager();
    
    addYears();
    addCollaboratingOrginizations();
    addOutcomeTypes();
    addCollaborationTypes();    
    
    if (isUpdate(im)) {
      log.debug("Collaboration save is update");
      im.updateCollaboration(new Id(this.id), this.collaboration);
    }
    else {
      log.debug("Collaboration save is create");
      im.createCollaboration(this.collaboration);
    }
    
    try {
      im.save();
    }
    catch (IOException e) {
      this.errorMessage = "Error: cannot save(),IO Exception";
      e.printStackTrace();
    }
    catch (JAXBException e) {
      this.errorMessage = "Error: cannot save(),JAXB Exception";
      e.printStackTrace();
    }
    return new RedirectResolution("/collaborations.jsp");
  }
  /**
   * Add Years from submitted values to collaboration objects.
   *
   */  
  
  private void addYears() {
    if (this.collaborationYears == null ) { 
      return;
    }        
    Years years = new Years();
    for (String y : this.collaborationYears) {
      log.debug("Adding year: " + y);      
      years.getYear().add(new BigInteger(y)) ;
//      this.collaboration.getYears().getYear().add(i);
    }    
    this.collaboration.setYears(years);
  }
  /**
   * Add collaboration type from submitted values to collaboration objects.
   *
   */
  private void addCollaborationTypes() {
    if (this.collaborationTypes == null ) { 
      return;
    }    
    log.debug("Adding newCollaborationTypes types.......");     
    CollaborationTypes newCollaborationTypes = new CollaborationTypes();
    for (String collaborationTypes 
              : IsernManager.splitStringToList(this.collaborationTypes, "\n")) {
      log.debug("Adding newCollaborationTypes type: " + collaborationTypes);      
      newCollaborationTypes.getCollaborationType().add(collaborationTypes);
    }    
    this.collaboration.setCollaborationTypes(newCollaborationTypes);
  }
  /**
   * Add Outcome type from submitted values to collaboration objects.
   *
   */  
  private void addOutcomeTypes() {
    if (this.outcomeTypes == null ) { 
      return;
    }        
    log.debug("Adding newOutcomeTypes types....");    
    OutcomeTypes newOutcomeTypes = new OutcomeTypes();
    for (String outcomeTypes : IsernManager.splitStringToList(this.outcomeTypes, "\n")) {
      log.debug("Adding outcome type: " + outcomeTypes);
      if (outcomeTypes != null )  { 
        newOutcomeTypes.getOutcomeType().add(outcomeTypes);
      }
    }    
    this.collaboration.setOutcomeTypes(newOutcomeTypes);
  }
  /**
   * Add Collaborating Orginizations from submitted values to collaboration objects.
   *
   */    
  private void addCollaboratingOrginizations() {
    if (this.collaboratingOrganizations == null ) { 
      System.out.println("Not collaboratingOrganizations");
      return;
    }    
    CollaboratingOrganizations newOrgs = new CollaboratingOrganizations();
    for (String org : this.collaboratingOrganizations) {
      log.debug("Adding org : " + org);      
      newOrgs.getCollaboratingOrganization().add(org);
    }    
    this.collaboration.setCollaboratingOrganizations(newOrgs);
    
    /*
    IsernManager im = new IsernManager();
    Collaboration oldCollaboration = im.getCollaboration(new Id(this.id));
    if (oldCollaboration != null) {
      this.collaboration.
          setCollaboratingOrganizations(oldCollaboration.getCollaboratingOrganizations());
    }
    */
  }

  /**
   * Tells program if the record is new or update.
   * 
   * @param im An IsernManager object.
   * @return True if the id exist and it is an update.
   */
  private boolean isUpdate(IsernManager im) {
    return (im.getCollaboration(new Id(this.id)) != null);
  }

}